﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Windows.Documents;


namespace JustTypeUI.Logic
{
    /// <summary>
    /// Main control logic
    /// Actuator of parsed commands
    /// 
    /// Main Author: Meng Kaizhi
    /// </summary>
    public class Control
    {
        //Singleton Control object
        private static Control _control;

        public bool reminder=true;
        
        private List<Task> taskList;
        public List<Task> TaskList
        { get { return taskList; } }

        private List<Task> doneList;
        public List<Task> DoneList
        { get { return doneList; } }

        private ResultDisplay resultList;
        public ResultDisplay ResultList
        { get { return resultList; }}
        public Feedback FeedbackValue
        { get { return feedbackValue; }}

        //history of commands
        private List<Command> commandList;
        
        private JustTypeUI.Data.DataStorage dataStorage;
        private Data.History history;
        private Feedback feedbackValue;
        

        /// <summary>
        /// constructor of Control and create objects of taskList, commandList, errorList and dataStorage.
        /// </summary>
        private Control()
        {
            taskList = new List<Task> { };
            doneList = new List<Task> { };
            resultList = new ResultDisplay();
            commandList = new List<Command> { };
            dataStorage = new JustTypeUI.Data.DataStorage();
            
            feedbackValue = new Feedback();
            history = new Data.History();

            Initialize();
        }

        /// <summary>
        /// 
        /// </summary>
        private void Initialize()
        {
            this.ReadFile();
        }

        public static Control GetInstance()
        {
            if (_control==null)
                _control = new Control();
            return _control;
        }

        /// <summary>
        /// sort the task list according to task deadline. If two tasks have the same deadline and it will sort by task name
        /// </summary>
        public void SortDeadline()
        {
            taskList.Sort(delegate(Task t1, Task t2)
            {
                if (t1.Deadline.CompareTo(t2.Deadline) != 0)
                    return t1.Deadline.CompareTo(t2.Deadline);
                else
                    return t1.Name.CompareTo(t2.Name);
            });

        }
        /// <summary>
        /// pass the user command to commandparser to get a formated command
        /// </summary>
        /// <param name="userCommand">command user inputs</param>
        /// <returns>the formated command after paserting</returns>
        public Command AnalyseCommand(string userCommand)
        {
            //parse the command and store information in a command object
            CommandParser parser = new CommandParser(userCommand);
            Command command = parser.GetCommand();
            return command;
        }
        /// <summary>
        /// Doing operation according to command type
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>the right command is operated successfully</returns>
        public bool ProcessCommand(Command command)
        {
            bool finish = false;

            switch (command.Type)
            {
                case "add":
                    finish = AddTask(command);
                    break;
                case "delete":
                    finish = DeleteTask(command);
                    break;
                case "search":
                    SearchTask(command);
                    break;
                case "edit":
                    finish = Edit(command);
                    break;
                case "clear":
                    finish = ClearTask();
                    break;
                case "home":
                    finish = true;
                    feedbackValue.FeedbackMessage = "";
                    break;
                case "done":
                    finish = DoneTask(command);
                    break;
                case "postpone":
                    finish = PostPoneTask(command);
                    break;
                case "undo":
                    finish = Undo();
                    break;
                case "redo":
                    finish = Redo();
                    break;
                default:
                    InvalidCommand();
                    break;
            }

            return finish;
        }

        private Boolean Undo()
        {
            bool result = history.GoBack(ref taskList, ref doneList);
            if (result)
            {
                FeedbackValue.FeedbackMessage = "undo successfully!";
                return true;
            }
            else
            {
                FeedbackValue.FeedbackMessage = "undo failed!";
                return false;
            }
        }

        private Boolean Redo()
        {
            bool result = history.GoFoward(ref taskList, ref doneList);
            if (result)
            {
                FeedbackValue.FeedbackMessage = "redo successfully!";
                return true;
            }
            else
            {
                FeedbackValue.FeedbackMessage = "redo failed!";
                return false;
            }
        }
        private bool PostPoneTask(Command command)
        {
            if (command.TaskKey.Count == 0)
            {
                if (command.TaskIndex.Count == 0)
                {
                    FeedbackValue.FeedbackMessage = Error.PostponeError.NO_INDEX;
                    return false;
                }

                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    if (command.TaskIndex[i] >= taskList.Count || command.TaskIndex[i] < 0)
                    {
                        FeedbackValue.FeedbackMessage = Error.PostponeError.INDEX_OVER_FLOW;
                        return false;
                    }
                }

                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    taskList[command.TaskIndex[i]].Deadline = taskList[command.TaskIndex[i]].Deadline.AddDays(1);
                }
                if (command.TaskIndex.Count == 1)
                    FeedbackValue.FeedbackMessage = "\""+taskList[command.TaskIndex[0]].Name+"\"" + " is postponed by one day";
                else
                    FeedbackValue.FeedbackMessage = "Tasks are postponed to tomorrow";

                SortDeadline();
                CheckStatus();
                return true;
            }
            else
            {
                PromptFurtherInfo("Postpone", command);
                return false;
            }

        }

        /// <summary>
        /// set the status of task is done already
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>whether done task is successful</returns>
        private bool DoneTask(Command command)
        {
            if (command.TaskKey.Count == 0)
            {
                if (command.TaskIndex.Count == 0)
                {
                    FeedbackValue.FeedbackMessage = Error.DoneError.NO_INDEX;
                    return false;
                }

                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    if (command.TaskIndex[i] >= taskList.Count || command.TaskIndex[i] < 0)
                    {
                        FeedbackValue.FeedbackMessage = Error.DoneError.INDEX_OVER_FLOW;
                        return false;
                    }
                }
                command.TaskIndex.Sort();
                if (command.TaskIndex.Count == 1)
                    FeedbackValue.FeedbackMessage = "\"" + taskList[command.TaskIndex[0]].Name + "\"" + " is done!";
                else
                    FeedbackValue.FeedbackMessage = "Tasks are done!";
                for (int i = command.TaskIndex.Count-1; i >= 0; i--)
                {
                    taskList[command.TaskIndex[i]].Status = 1;
                    doneList.Add(taskList[command.TaskIndex[i]]);
                    taskList.RemoveAt(command.TaskIndex[i]);
                }
                
                SortDeadline();
                return true;
            }
            else
            {
                PromptFurtherInfo("Done", command);
                return false;
            }
            
        }

        /// <summary>
        /// print the error message of invalid command
        /// </summary>
        private void InvalidCommand()
        {
            FeedbackValue.FeedbackMessage = Error.CommandError.INVALID_COMMAND;
        }
        /// <summary>
        /// add a task in list
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>whether addition is successful</returns>
        private bool AddTask(Command command)
        {
            Task newTask = new Task();
            bool finish;

            if (command.TaskDeadline == DateTime.MinValue)
            {
                TimeSpan ts = new TimeSpan(0, DateTime.Now.Minute, DateTime.Now.Second);
                if (DateTime.Now.Minute > 30)
                {
                    newTask.Deadline=DateTime.Now.AddHours(2) - ts;
                }
                else
                {
                    newTask.Deadline = DateTime.Now.AddHours(1) - ts;
                }
            }
            else
            {
                newTask.Deadline = command.TaskDeadline;
            }
            if (command.TaskLocation!=null && command.TaskLocation!="")
                newTask.Location = "@ "+command.TaskLocation;

            if (command.TaskName == ""||command.TaskName==null)
            {
                finish = false;
                FeedbackValue.FeedbackMessage = Error.AddError.NO_TASK_NAME;
            }
            else
            {               
                newTask.Name = command.TaskName;
                taskList.Add(newTask);
                FeedbackValue.FeedbackMessage = "\"" + command.TaskName +"\"" + " is added!";
                SortDeadline();
                CheckStatus();
                finish = true;
            }

            return finish;
        }
        /// <summary>
        /// Delete a task(tasks) in the list. If user delete by index, it can delete directly.
        /// If user delete by taskKey, it will show the tasks related  to the taskKey and 
        /// let user choose which task should be deleted.
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>whether deletion is successful</returns>
        private bool DeleteTask(Command command)
        {
            if (command.TaskKey.Count == 0)
            {
                if (command.TaskIndex.Count == 0)
                {
                    FeedbackValue.FeedbackMessage = Error.DeleteError.NO_INDEX;
                    return false;
                }
                //check whether there are indexs out of range
                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    if (command.TaskIndex[i] >= taskList.Count || command.TaskIndex[i] < 0)
                    {
                        FeedbackValue.FeedbackMessage = Error.DeleteError.INDEX_OVER_FLOW;
                        return false;
                    }
                }
                if (command.TaskIndex.Count == 1)
                    FeedbackValue.FeedbackMessage = "\"" + taskList[command.TaskIndex[0]].Name +"\"" + " is deleted!";
                else
                    FeedbackValue.FeedbackMessage = "Tasks are deleted!";
                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    taskList.RemoveAt(command.TaskIndex[i]);
                }
                

                SortDeadline();
                CheckStatus();
                return true;
            }
            else
            {
                PromptFurtherInfo("Delete", command);
                return false;
            }
        }

        private void PromptFurtherInfo(String action, Command command)
        {
            SearchTask(command);
            if (FeedbackValue.FeedbackMessage!="No result found!\n")
                FeedbackValue.FeedbackMessage += action+" needs more instruction! Please choose the result!";
        }

        /// <summary>
        /// edit a task in the list
        /// </summary>
        /// <param name="command">the targat command</param>
        /// <returns>whether edit successfully</returns>
        private bool Edit(Command command)
        {
            if (command.TaskIndex.Count == 0 && command.TaskKey.Count == 0)
            {
                FeedbackValue.FeedbackMessage = Error.EditError.NO_INDEX+"\n";
                return false;
            }
            else
            {
                if (command.TaskIndex.Count == 1)
                {
                    if (command.TaskIndex[0] >= taskList.Count || command.TaskIndex[0] < 0)
                    {
                        FeedbackValue.FeedbackMessage = Error.EditError.INDEX_OVER_FLOW;
                        return false;
                    }
                    FeedbackValue.FeedbackMessage = "\"" + taskList[command.TaskIndex[0]].Name + "\"" + " is edited!";
                    if (command.TaskName != null&&command.TaskName!="")
                        taskList[command.TaskIndex[0]].Name = command.TaskName;

                    if (command.TaskDeadline != DateTime.MinValue)
                        taskList[command.TaskIndex[0]].Deadline = command.TaskDeadline;//exception handling here

                    if (command.TaskLocation != null && command.TaskLocation != "")
                        taskList[command.TaskIndex[0]].Location = "@ "+command.TaskLocation;
                    
                        
                    
                    // these three functions can be grouped together
                    SortDeadline();
                    CheckStatus();

                    return true;
                }
                else
                {
                    PromptFurtherInfo("Edit", command);
                    return false;
                }
            }
        }
        /// <summary>
        /// the search tasks in list which related to input taskKey in command. Also it can search by some deadline like “today”, “Mon”, etc. 
        /// </summary>
        /// <param name="command">targat command for searching</param>
        /// <returns>the list of task which follow the sesarch condition</returns>
        private void SearchTask(Command command)
        {
            bool match;
            List<int> resultTaskNumber = new List<int> { };
            List<string> keyWords = new List<string> { };

            foreach (string str in command.TaskKey)
            {
                keyWords.Add(str.ToUpper());
            }

            for (int i = 0; i < taskList.Count; i++)
            {
                match = true;
                string singleTask = "";
                singleTask += taskList[i].Name.ToUpper() + " ";

                if (taskList[i].Location != null)
                {
                    singleTask += taskList[i].Location.ToUpper() + " ";
                }

      

                if (taskList[i].Deadline.Date == DateTime.Today.Date)
                {
                    singleTask += " TODAY";
                }

                if (taskList[i].Deadline.Date == DateTime.Today.Date.AddDays(1))
                {
                    singleTask += " TMR"+" TOMORROW";
                }

                singleTask += taskList[i].Deadline.ToString("D").ToUpper() + " ";

                for (int j = 0; j < keyWords.Count; j++)
                {
                    if (singleTask.IndexOf(keyWords[j]) == -1)
                    {
                        match = false;
                        break;
                    }
                }
                if (match) resultTaskNumber.Add(i);
            }
            if (resultTaskNumber.Count == 0)
                FeedbackValue.FeedbackMessage = "No result found!\n";
            else
                FeedbackValue.FeedbackMessage = "Above are found results\n";

            ConvertListToResult(taskList, resultTaskNumber, resultList);
        }

        /// <summary>
        /// delete all task
        /// </summary>
        private bool ClearTask()
        {
            taskList.Clear();
            FeedbackValue.FeedbackMessage = "All tasks are deleted!";
            return true;
        }


        public void ShowCompleted()
        {
            List<int> temp = new List<int>();
            for (int i = 0; i < doneList.Count; i++)
                temp.Add(i);
            ConvertListToResult(doneList, temp, resultList);
        }
        
        
       
        /// <summary>
        /// check the task is done or on schedule
        /// </summary>
        private void CheckStatus()
        {
            foreach (Task todo in taskList)
            {
                TimeSpan ts = Convert.ToDateTime("0:30:00") - DateTime.Today;
                if (todo.Status != 1)
                {
                    if (todo.Deadline < DateTime.Now)
                    {
                        todo.Status = 3;
                        continue;
                    }

                    if ((todo.Deadline - DateTime.Now) < ts)
                    {
                        todo.Status = 2;
                        reminder = true;
                        continue;
                    }

                    if ((todo.Deadline - DateTime.Now) > ts)
                    {
                        todo.Status = 0;
                        reminder = true;
                        continue;
                    }
                }
            }
        }
       
        private void ReadFile()
        {
            taskList = dataStorage.ReadDataFromXMLFile("data");
            if (taskList == null)
                taskList = new List<Task> { };
            doneList = dataStorage.ReadDataFromXMLFile("completed");
            if (doneList == null)
                doneList = new List<Task> { };
            CheckStatus();
            history.Put(taskList, doneList);
        }

        public void WriteFile()
        {
            dataStorage.WriteDataToFile("data", taskList);
            dataStorage.WriteDataToFile("completed", doneList);
        }

        public void Process(string userCommand)
        {
            Command analysedCommand = AnalyseCommand(userCommand);
            
            if (ProcessCommand(analysedCommand) == true)
            {
                Refresh();
                WriteFile();
                //write in history
                if ( analysedCommand.Type == "add" || analysedCommand.Type == "delete" || analysedCommand.Type == "clear" || analysedCommand.Type == "edit" || analysedCommand.Type == "postpone" ||analysedCommand.Type =="done")
                    history.Put(taskList, doneList);
            }
            Data.Log.log(feedbackValue.ToString());
        }

        private void ConvertListToResult(List<Task> list,List<int> index, ResultDisplay result)
        {
            result.ResultTaskIndex.Clear();
            result.ResultTasks.Clear();

            for (int i = 0; i < index.Count; i++)
            {
                result.ResultTaskIndex.Add(index[i] + 1);
            }

            for (int i = 0; i < list.Count; i++)
                if (index.Contains(i))
                {
                    ResultTask rt = new ResultTask(list[i], i+1);
                    result.ResultTasks.Add(rt);
                }
        }

        public void Refresh()
        {
            List<int> temp = new List<int>();
            for (int i=0; i<taskList.Count; i++)
                temp.Add(i);
            ConvertListToResult(taskList, temp, resultList);
        }
    }
}
